#pragma once

#include <AVUIItemStructList.h>
#include <AVUIVisual.h>
#include <AVUIDependencyProperty.h>
#include <AVUIDrawingContext.h>
#include <AVUIThickness.h>
#include <AVUISize.h>
#include <AVUIMouse.h>
#include <AVUIKeyboard.h>
#include <AVUITextCompositionManager.h>
#include <AVUIFocusManager.h>
#include <AVUIIInputElement.h>
#include <AVUIIAnimatable.h>

namespace AVUI {

class PresentationSource : public Object
{
public:
    DECLARE_ELEMENT(PresentationSource, Object);

    static Visual* CriticalFromVisual(DependencyObject* pVisual);
};

class LayoutTransformData : public Object
{
public:
    LayoutTransformData() { }

    Transform* get_Transform() { return m_pTransform; }

    void CreateTransformSnapshot(Transform* pSourceTransform);
    Size get_UntransformedDS() { return m_untransformedDS; }
    void set_UntransformedDS(const Size& size) { m_untransformedDS = size; }

    DECLARE_ELEMENT(LayoutTransformData, Object);

private:
    Size m_untransformedDS;
    TRefCountedPtr<Transform> m_pTransform;
};

class MeasureData : public Object
{
public:
    MeasureData(MeasureData* pData) : m_availableSize(pData->m_availableSize), m_viewport(pData->m_viewport) { };
    MeasureData(const Size& availableSize, const Rect& viewport) : m_availableSize(availableSize), m_viewport(viewport) { }
    bool IsCloseTo(MeasureData* pOther) { if(pOther == NULL) return false; return (Float::AreClose(get_AvailableSize(), pOther->get_AvailableSize()) && Float::AreClose(get_Viewport(), pOther->get_Viewport())); }

    Size get_AvailableSize() { return m_availableSize; }
    void set_AvailableSize(const Size& availableSize) { m_availableSize = availableSize; }

    bool get_HasViewport() { return get_Viewport() != Rect::get_Empty(); }
    Rect get_Viewport() { return m_viewport; }
    void set_Viewport(const Rect& rect) { m_viewport = rect; }

    DECLARE_ELEMENT(MeasureData, Object);

private:

    Size m_availableSize;
    Rect m_viewport;

};


class SizeBox : public Object
{
public:
    DECLARE_ELEMENT(SizeBox, Object);

    // Methods
    SizeBox(Size size) : m_width(size.get_Width()), m_height(size.get_Height()) { };
    SizeBox(float width, float height) : m_width(width), m_height(height) { }

    // Properties
    float get_Width() { return m_width; }
    void set_Width(float width) { m_width = width; }
    float get_Height() { return m_height; }
    void set_Height(float height) { m_height = height; }

private:
    float m_height;
    float m_width;

};

class AnimationTimeline;
class MouseEventArgs;
class MouseButtonEventArgs;
class MouseWheelEventArgs;
class ResourceDictionary;
class INameScope;
class CommandBinding;
class ExecutedRoutedEventArgs;
class RequestBringIntoViewEventArgs;
class TriggerCollection;
class BindingExpressionBase;
class BindingBase;
class QueryCursorEventArgs;
class Cursor;
class SizeChangedInfo;
class FrameworkTemplate;
class FocusWithinProperty;
class MouseCaptureWithinProperty;
class MouseOverProperty;
class ContextMenu;
class ToolTip;
class ContentElement;
class UIElement3D;
class TraversalRequest;
class Effect;
class MeasureData;
class IContentHost;

class UIElement : public Visual, public IInputElement, public IAnimatable
{
    friend class VisualBrush;
    friend class Mouse;
    friend class XamlParser;
    friend class CommandManager;
    friend class KeyboardDevice;
    friend class EventTrigger;
    friend class ItemsControl;
    friend class Helper;
    friend class ReverseInheritProperty;
    friend class FocusWithinProperty;
    friend class MouseCaptureWithinProperty;
    friend class MouseOverProperty;
    friend class ElementObjectRef;
    friend class PopupRoot;
    friend class Visual;
    friend class FrameworkObject;
    friend class FrameworkElement;
    friend class FocusManager;
    friend class DeferredElementTreeState;
    friend class ContextLayoutManager;
public:
    DECLARE_ELEMENT(UIElement, Visual);

    UIElement();
    virtual ~UIElement();

    void Measure(const Size& constraintSize);
    void Arrange(const Rect& finalRect);

    bool CaptureMouse();
    void ReleaseMouseCapture();

    const Size& get_DesiredSize() { static Size defaultSize; return get_VisibilityCache() == Visibility::Collapsed ? defaultSize : m_desiredSize; };
    const Size& get_RenderSize() { static Size defaultSize; return get_VisibilityCache() == Visibility::Collapsed ? defaultSize : m_renderSize; };
    void set_RenderSize(const Size& renderSize) { m_renderSize = renderSize; } 
    const Size& get_PreviousConstraint() { return m_previousAvailableSize; };
    const Rect& get_PreviousArrangeRect() { return m_finalRect; };

    void ApplyAnimationClock(DependencyProperty* pProperty, AnimationClock *pClock) { ApplyAnimationClock(pProperty,pClock,AVUI::HandoffBehavior::SnapshotAndReplace); }
    void ApplyAnimationClock(DependencyProperty* pProperty, AnimationClock *pClock, HandoffBehavior::Enum);
    void BeginAnimation(DependencyProperty* pProperty, AnimationTimeline* pTimeline) { BeginAnimation(pProperty,pTimeline,AVUI::HandoffBehavior::SnapshotAndReplace); }
    void BeginAnimation(DependencyProperty* pProperty, AnimationTimeline* pTimeline, HandoffBehavior::Enum handoffBehavior);

    Object* GetAnimationBaseValue(DependencyProperty* pDP);

    void InvalidateMeasure();
    void InvalidateMeasureInternal() { set_MeasureDirty(true); }
    void InvalidateArrange();
    void InvalidateArrangeInternal() { set_ArrangeDirty(true); }

    void InvalidateVisual();

    bool get_IsMeasureValid() const { return !get_MeasureDirty(); }
    bool get_IsArrangeValid() const { return !get_ArrangeDirty(); }

    void UpdateLayout();

    DECLARE_DEPENDENCY_PROPERTY(RenderTransform);
    TRefCountedPtr<Transform> get_RenderTransform() { return (Transform*)(Object*)GetValue(get_RenderTransformProperty()); }
    void set_RenderTransform(Transform* pValue) { SetValue(get_RenderTransformProperty(), (Object*)pValue); } 

    DECLARE_DEPENDENCY_PROPERTY(Clip);
    TRefCountedPtr<Geometry> get_Clip() { return (Geometry*)(Object*)GetValue(get_ClipProperty()); }
    void set_Clip(Geometry* pValue) { SetValue(get_ClipProperty(), (Object*)pValue); } 

    DECLARE_DEPENDENCY_PROPERTY(Opacity);
    float get_Opacity() { return AVUI::UnboxValue<float>(GetValue(get_OpacityProperty())); }
    void set_Opacity(float value) { SetValue(get_OpacityProperty(), AVUI::BoxValue(value)); } 

    DECLARE_DEPENDENCY_PROPERTY(Focusable);
    bool get_Focusable() { return AVUI::UnboxValue<bool>(GetValue(get_FocusableProperty())); }
    void set_Focusable(bool value) { SetValue(get_FocusableProperty(), AVUI::BoxValue(value)); } 

    DECLARE_READONLY_DEPENDENCY_PROPERTY(IsMouseOver);
    bool get_IsMouseOver() { return AVUI::UnboxValue<bool>(GetValue(get_IsMouseOverProperty())); }

    DECLARE_READONLY_DEPENDENCY_PROPERTY(IsMouseCaptureWithin);
    bool get_IsMouseCaptureWithin() { return AVUI::UnboxValue<bool>(GetValue(get_IsMouseCaptureWithinProperty())); }

    DECLARE_READONLY_DEPENDENCY_PROPERTY(IsMouseCaptured);
    bool get_IsMouseCaptured() { return AVUI::UnboxValue<bool>(GetValue(get_IsMouseCapturedProperty())); }

    DECLARE_READONLY_DEPENDENCY_PROPERTY(IsFocused);
    bool get_IsFocused() { return AVUI::UnboxValue<bool>(GetValue(get_IsFocusedProperty())); }

    DECLARE_READONLY_DEPENDENCY_PROPERTY(IsKeyboardFocused);
    bool get_IsKeyboardFocused() { return AVUI::UnboxValue<bool>(GetValue(get_IsKeyboardFocusedProperty())); }

    DECLARE_READONLY_DEPENDENCY_PROPERTY(IsKeyboardFocusWithin);
    bool get_IsKeyboardFocusWithin() { return AVUI::UnboxValue<bool>(GetValue(get_IsKeyboardFocusWithinProperty())); }

    DECLARE_READONLY_DEPENDENCY_PROPERTY(IsVisible);

    DECLARE_DEPENDENCY_PROPERTY(IsHitTestVisible);
    bool get_IsHitTestVisible() { return AVUI::UnboxValue<bool>(GetValue(get_IsHitTestVisibleProperty())); }
    void set_IsHitTestVisible(bool value) { SetValue(get_IsHitTestVisibleProperty(), AVUI::BoxValue(value)); } 

    DECLARE_DEPENDENCY_PROPERTY(IsEnabled);
    bool get_IsEnabled() { return AVUI::UnboxValue<bool>(GetValue(get_IsEnabledProperty())); }
    void set_IsEnabled(bool value) { SetValue(get_IsEnabledProperty(), AVUI::BoxValue(value)); } 

    DECLARE_DEPENDENCY_PROPERTY(AllowDrop);
    bool get_AllowDrop() { return AVUI::UnboxValue<bool>(GetValue(get_AllowDropProperty())); }
    void set_AllowDrop(bool value) { SetValue(get_AllowDropProperty(), AVUI::BoxValue(value)); } 

    DECLARE_DEPENDENCY_PROPERTY(RenderTransformOrigin);
    Point get_RenderTransformOrigin() { return AVUI::UnboxValue<Point>(GetValue(get_RenderTransformOriginProperty())); }
    void set_RenderTransformOrigin(const Point& value) { SetValue(get_RenderTransformOriginProperty(), AVUI::BoxValue(value)); } 

    DECLARE_DEPENDENCY_PROPERTY(ClipToBounds);
    bool get_ClipToBounds() { return AVUI::UnboxValue<bool>(GetValue(get_ClipToBoundsProperty())); }
    void set_ClipToBounds(bool value) { SetValue(get_ClipToBoundsProperty(), AVUI::BoxValue(value)); } 

    DECLARE_DEPENDENCY_PROPERTY(SnapsToDevicePixels);
    bool get_SnapsToDevicePixels() { return AVUI::UnboxValue<bool>(GetValue(get_SnapsToDevicePixelsProperty())); }
    void set_SnapsToDevicePixels(bool value) { SetValue(get_SnapsToDevicePixelsProperty(), AVUI::BoxValue(value)); } 

    DECLARE_DEPENDENCY_PROPERTY(Visibility);
    Visibility::Enum get_Visibility() { return (Visibility::Enum)(AVUI::UnboxValue<int>(GetValue(get_VisibilityProperty()))); }
    void set_Visibility(Visibility::Enum value) { SetValue(get_VisibilityProperty(), AVUI::BoxValue<int>(value)); } 

    DECLARE_DEPENDENCY_PROPERTY(OpacityMask);
    TRefCountedPtr<Brush> get_OpacityMask() { return (Brush*)(Object*)GetValue(get_OpacityMaskProperty()); }
    void set_OpacityMask(Brush* pValue) { SetValue(get_OpacityMaskProperty(), (Object*)pValue); } 

    DECLARE_DEPENDENCY_PROPERTY(Effect);
    TRefCountedPtr<Effect> get_Effect() { return (Effect*)(Object*)GetValue(get_EffectProperty()); }
    void set_Effect(Effect* pValue) { SetValue(get_EffectProperty(), (Object*)pValue); } 

    ALIAS_ROUTED_EVENT(Mouse, GotMouseCapture, MouseEventArgs);
    ALIAS_ROUTED_EVENT(Mouse, LostMouseCapture, MouseEventArgs);
    ALIAS_ROUTED_EVENT(Mouse, PreviewMouseMove,  MouseEventArgs);
    ALIAS_ROUTED_EVENT(Mouse, MouseMove,  MouseEventArgs);
    ALIAS_ROUTED_EVENT(Mouse, PreviewMouseDown,  MouseButtonEventArgs);
    ALIAS_ROUTED_EVENT(Mouse, MouseDown,  MouseButtonEventArgs);
    ALIAS_ROUTED_EVENT(Mouse, PreviewMouseUp,    MouseButtonEventArgs);
    ALIAS_ROUTED_EVENT(Mouse, MouseUp,    MouseButtonEventArgs);
    ALIAS_ROUTED_EVENT(Mouse, MouseWheel, MouseEventArgs);
    ALIAS_ROUTED_EVENT(Mouse, MouseEnter, MouseEventArgs);
    ALIAS_ROUTED_EVENT(Mouse, MouseLeave, MouseEventArgs);
    ALIAS_ROUTED_EVENT(Mouse, QueryCursor, QueryCursorEventArgs);

    ALIAS_ROUTED_EVENT(Keyboard, KeyDown, KeyEventArgs);
    ALIAS_ROUTED_EVENT(Keyboard, KeyUp, KeyEventArgs);
    ALIAS_ROUTED_EVENT(TextCompositionManager, TextInput, TextCompositionEventArgs);


    IMPLEMENT_ROUTED_EVENT(PreviewMouseLeftButtonDown, MouseButtonEventArgs, RoutingStrategy::Direct);
    IMPLEMENT_ROUTED_EVENT(MouseLeftButtonDown, MouseButtonEventArgs, RoutingStrategy::Direct);
    IMPLEMENT_ROUTED_EVENT(PreviewMouseLeftButtonUp, MouseButtonEventArgs, RoutingStrategy::Direct);
    IMPLEMENT_ROUTED_EVENT(MouseLeftButtonUp, MouseButtonEventArgs, RoutingStrategy::Direct);
    IMPLEMENT_ROUTED_EVENT(PreviewMouseRightButtonDown, MouseButtonEventArgs, RoutingStrategy::Direct);
    IMPLEMENT_ROUTED_EVENT(MouseRightButtonDown, MouseButtonEventArgs, RoutingStrategy::Direct);
    IMPLEMENT_ROUTED_EVENT(PreviewMouseRightButtonUp, MouseButtonEventArgs, RoutingStrategy::Direct);
    IMPLEMENT_ROUTED_EVENT(MouseRightButtonUp, MouseButtonEventArgs, RoutingStrategy::Direct);

    IMPLEMENT_ROUTED_EVENT(GotFocus, RoutedEventArgs, RoutingStrategy::Bubble);
    IMPLEMENT_ROUTED_EVENT(LostFocus, RoutedEventArgs, RoutingStrategy::Bubble);

    IMPLEMENT_EVENT(IsKeyboardFocusWithinChanged, DependencyPropertyChangedEventArgs);
    IMPLEMENT_EVENT(IsMouseCaptureWithinChanged, DependencyPropertyChangedEventArgs);
    IMPLEMENT_EVENT(IsMouseCapturedChanged, DependencyPropertyChangedEventArgs);

    IMPLEMENT_EVENT(IsEnabledChanged, DependencyPropertyChangedEventArgs);
    IMPLEMENT_EVENT(IsHitTestVisibleChanged, DependencyPropertyChangedEventArgs);
    IMPLEMENT_EVENT(IsVisibleChanged, DependencyPropertyChangedEventArgs);

    void RaiseEvent(RoutedEventArgs* pArgs);

    static void AddHandler(DependencyObject* pD, RoutedEvent* pRoutedEvent, Delegate* pEventHandler);
    static void RemoveHandler(DependencyObject* pD, RoutedEvent* pRoutedEvent, Delegate* pEventHandler);

    void AddHandler(RoutedEvent* pRoutedEvent, Delegate* pEventHandler) { AddHandler(pRoutedEvent, pEventHandler, false); }
    void RemoveHandler(RoutedEvent* pRoutedEvent, Delegate* pEventHandler);

    void AddHandler(RoutedEvent* pRoutedEvent, Delegate* pEventHandler, bool handledEventsToo);

    virtual bool BlockReverseInheritance() { return false; }

    bool get_MeasureDirty() const { return ReadFlag(CoreFlags::MeasureDirty); }
    bool get_ArrangeDirty() const { return ReadFlag(CoreFlags::ArrangeDirty); }
    bool get_NeverMeasured() const { return ReadFlag(CoreFlags::NeverMeasured); }
    bool get_NeverArranged() const { return ReadFlag(CoreFlags::NeverArranged); }

    void set_BypassLayoutPolicies(bool bypassLayoutPolicies) { WriteFlag(CoreFlags::BypassLayoutPolicies, bypassLayoutPolicies); }
    bool get_BypassLayoutPolicies() const { return ReadFlag(CoreFlags::BypassLayoutPolicies); }

    void set_AreTransformsClean(bool transformsAreClean) { WriteFlag(CoreFlags::TransformsAreClean, transformsAreClean); }
    bool get_AreTransformsClean() const { return ReadFlag(CoreFlags::TransformsAreClean); }

    bool get_ClipToBoundsCache() const { return ReadFlag(CoreFlags::ClipToBounds); }
    void set_ClipToBoundsCache(bool clipToBounds) { WriteFlag(CoreFlags::ClipToBounds, clipToBounds); }

    bool get_IsVisible() const { return m_isVisibleCache; }

    bool Focus();

    UIElement* GetUIParentWithinLayoutIsland() { return object_cast<UIElement>(get_VisualParent()); }

    virtual Object* get_Object() { return this; }

    /// <summary>Attempts to move focus from this element to another element. The direction to move focus is specified by a guidance direction, which is interpreted within the organization of the visual parent for this element. </summary>
    /// <returns>true if the requested traversal was performed; otherwise, false. </returns>
    /// <param name="request">A traversal request, which contains a property that indicates either a mode to traverse in existing tab order, or a direction to move visually.</param>
    virtual bool MoveFocus(TraversalRequest* pRequest) { return false; }

    /// <summary>When overridden in a derived class, returns the element that would receive focus for a specified focus traversal direction, without actually moving focus to that element.</summary>
    /// <returns>The element that would have received focus if <see cref="M:System.Windows.UIElement.MoveFocus(System.Windows.Input.TraversalRequest)" /> were actually invoked.</returns>
    /// <param name="direction">The direction of the requested focus traversal.</param>
    virtual DependencyObject* PredictFocus(FocusNavigationDirection::Enum direction) { return NULL; }

 
    bool get_HasAnimatedProperties() { VerifyAccess(); return get_IAnimatable_HasAnimatedProperties(); }

    MeasureData* get_MeasureData() { return NULL; }
    void set_MeasureData(MeasureData* pMeasureData) { }

    MeasureData* get_PreviousMeasureData() { return NULL; }
    void set_PreviousMeasureData(MeasureData* pMeasureData) { }


    TRefCountedPtr<Object> MeasureRequest;
    TRefCountedPtr<Object> ArrangeRequest;

    DependencyObject* GetUIParentNo3DTraversal();

    void add_LayoutUpdated(EventHandler* pValue);
    void remove_LayoutUpdated(EventHandler* pValue);

protected:

    virtual void OnRender(DrawingContext& drawingContext);
    virtual void OnChildDesiredSizeChanged(UIElement* pUIElement); 
    virtual void OnVisualParentChanged(DependencyObject* pOldParent);
    virtual void OnIsMouseCaptureWithinChanged(DependencyPropertyChangedEventArgs* pArgs) { } // NO IMPLEMENTATION IN BASE
    virtual void OnIsMouseCapturedChanged(DependencyPropertyChangedEventArgs* pE) { }
    virtual void OnIsKeyboardFocusWithinChanged(DependencyPropertyChangedEventArgs* pArgs) { } // NO IMPLEMENTATION IN BASE

    virtual bool BuildRouteCore(EventRoute* pRoute, RoutedEventArgs* pArgs) { return false; } // NO IMPLEMENTATION IN BASE
    virtual void AddToEventRouteCore(EventRoute* pRoute, RoutedEventArgs* pArgs) { };

    virtual void OnGotMouseCapture(MouseEventArgs* pArgs) { };
    virtual void OnLostMouseCapture(MouseEventArgs* pArgs) { };

    virtual void OnPreviewMouseMove(MouseEventArgs* pArgs) { };
    virtual void OnMouseMove(MouseEventArgs* pArgs) { };
    virtual void OnPreviewMouseDown(MouseButtonEventArgs* pArgs) { };
    virtual void OnMouseDown(MouseButtonEventArgs* pArgs) { };
    virtual void OnPreviewMouseUp(MouseButtonEventArgs* pArgs) { };
    virtual void OnMouseUp(MouseButtonEventArgs* pArgs) { };

    virtual void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs* pArgs) { };
    virtual void OnMouseLeftButtonDown(MouseButtonEventArgs* pArgs) { };
    virtual void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs* pArgs) { };
    virtual void OnMouseLeftButtonUp(MouseButtonEventArgs* pArgs) { };
    virtual void OnPreviewMouseRightButtonDown(MouseButtonEventArgs* pArgs) { };
    virtual void OnMouseRightButtonDown(MouseButtonEventArgs* pArgs) { };
    virtual void OnPreviewMouseRightButtonUp(MouseButtonEventArgs* pArgs) { };
    virtual void OnMouseRightButtonUp(MouseButtonEventArgs* pArgs) { };

    virtual void OnMouseWheel(MouseWheelEventArgs* pArgs) { };
    virtual void OnMouseEnter(MouseEventArgs* pArgs) { };
    virtual void OnMouseLeave(MouseEventArgs* pArgs) { };
    virtual void OnQueryCursor(QueryCursorEventArgs* pArgs) { };

    virtual void OnKeyDown(KeyEventArgs* pArgs) { };
    virtual void OnKeyUp(KeyEventArgs* pArgs) { };
    virtual void OnTextInput(TextCompositionEventArgs* pArgs) { };

    virtual void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs* e) { }
    virtual void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs* e) { }

    virtual void OnGotFocus(RoutedEventArgs* pArgs);
    virtual void OnLostFocus(RoutedEventArgs* pArgs);

    virtual TRefCountedPtr<Geometry> GetLayoutClip(const Size& layoutSlotSize);

    virtual DependencyObject* GetUIParentCore() { return NULL; }
	virtual TRefCountedPtr<HitTestResult> HitTestCore(PointHitTestParameters* pHitTestParameters);

    virtual void OnRenderSizeChanged(SizeChangedInfo* pSizeInfo) { };
    virtual void OnAddHandler(RoutedEvent* pRoutedEvent, Delegate* pHandler) { };
    virtual void OnRemoveHandler(RoutedEvent* pRoutedEvent, Delegate* pHandler) { };
    virtual void OnPresentationSourceChanged(bool attached) { };

private:

    void addLayoutUpdatedHandler(EventHandler* pHandler, Object* pItem);
    Object* getLayoutUpdatedHandler(EventHandler* pDO);
    void removeLayoutUpdatedHandler(EventHandler* pDO);

    DependencyObject* GetUIParent();
    DependencyObject* GetUIParent(bool continuePastVisualTree);
    void GetUIParentOrICH(UIElement** ppUIParent, IContentHost** ppIContentHost);
    bool markForSizeChangedIfNeeded(const Size& oldSize, const Size& newSize);

    void InputHitTest(const Point& pt, DependencyObject** ppEnabledHit, DependencyObject** ppRawHit);
    void InputHitTestFilterCallback(Object* pPotentialHitTestTarget, HitTestFilterArgs* pArgs);

    static FocusWithinProperty* get_FocusWithinProperty();
    static MouseCaptureWithinProperty* get_MouseCaptureWithinProperty();
    static MouseOverProperty* get_MouseOverProperty();

    void RaiseIsMouseCaptureWithinChanged(DependencyPropertyChangedEventArgs* pArgs);
    void RaiseIsMouseCapturedChanged(DependencyPropertyChangedEventArgs* pArgs);
    void RaiseIsKeyboardFocusWithinChanged(DependencyPropertyChangedEventArgs* pArgs);

    static void SynchronizeForceInheritProperties(UIElement* pUIElement, ContentElement* pContentElement, UIElement3D* pUIElement3D, DependencyObject* pParent);
    static void SynchronizeReverseInheritPropertyFlags(DependencyObject* pOldParent, bool isCoreParent) { };

    static void InvalidateForceInheritPropertyOnChildren(Visual* pVisual, DependencyProperty* pDependencyProperty);

    static void StaticConstructor();
    void PrivateSetFocused(bool isFocused);

    virtual void EvaluateAnimatedValueCore(DependencyProperty* pDP, PropertyMetadata* pMetadata, EffectiveValueEntry* pNewEntry);

    virtual Size MeasureCore(const Size& availableSize) { return Size(); }
    virtual void ArrangeCore(const Rect& finalRect);

    void WriteFlag(unsigned int flag, bool value) { m_flags = (value ? m_flags | flag : m_flags & ~flag); }
    bool ReadFlag(unsigned int flag) const { return (m_flags & flag) != 0; }

    void set_MeasureDirty(bool measureDirty) { WriteFlag(CoreFlags::MeasureDirty, measureDirty); }
    void set_ArrangeDirty(bool arrangeDirty) { WriteFlag(CoreFlags::ArrangeDirty, arrangeDirty); }
    void set_MeasureInProgress(bool measureInProgress) { WriteFlag(CoreFlags::MeasureInProgress, measureInProgress); }
    bool get_MeasureInProgress() const { return ReadFlag(CoreFlags::MeasureInProgress); }
    void set_MeasureDuringArrange(bool measureDuringArrange) { WriteFlag(CoreFlags::MeasureDuringArrange, measureDuringArrange); }
    bool get_MeasureDuringArrange() const { return ReadFlag(CoreFlags::MeasureDuringArrange); }
    void set_NeverMeasured(bool neverMeasured) { WriteFlag(CoreFlags::NeverMeasured, neverMeasured); }

    void set_RenderingInvalidated(bool renderingInvalidated) { WriteFlag(CoreFlags::RenderingInvalidated, renderingInvalidated); }
    bool get_RenderingInvalidated() const { return ReadFlag(CoreFlags::RenderingInvalidated); }

    void set_HasTemplateGeneratedSubTree(bool hasTemplateGeneratedSubTree) { WriteFlag(CoreFlags::HasTemplateGeneratedSubTree, hasTemplateGeneratedSubTree); }

    void set_NeedsClipBounds(bool needsClipBounds) { WriteFlag(CoreFlags::NeedsClipBounds, needsClipBounds); }
    bool get_NeedsClipBounds() const { return ReadFlag(CoreFlags::NeedsClipBounds); }

    void set_NeverArranged(bool neverArranged) { WriteFlag(CoreFlags::NeverArranged, neverArranged); }
    void set_ArrangeInProgress(bool arrangeInProgress) { WriteFlag(CoreFlags::ArrangeInProgress, arrangeInProgress); }
    bool get_ArrangeInProgress() const { return ReadFlag(CoreFlags::ArrangeInProgress); }

    bool IsRenderable();
    void ensureClip(const Size& layoutSlotSize);

    Point GetRenderTransformOrigin();

    virtual TRefCountedPtr<Object> AdjustEventSource(RoutedEventArgs* pArgs) { return NULL; } // NO IMPLEMENTATION ON BASE
    static void BuildRouteHelper(DependencyObject* pDO, EventRoute* pRoute, RoutedEventArgs* pArgs);
    void AddToEventRoute(EventRoute* pRoute, RoutedEventArgs* pArgs);
    void BuildRoute(EventRoute* pRoute, RoutedEventArgs* pArgs);
    void AddListenersToRoute(EventRoute* pRoute, RoutedEvent* pEvent);


    static void OnPreviewMouseMoveThunk(Object* pSender, MouseEventArgs* pArgs) { ((UIElement*) pSender)->OnPreviewMouseMove(pArgs); }
    static void OnMouseMoveThunk(Object* pSender, MouseEventArgs* pArgs) { ((UIElement*) pSender)->OnMouseMove(pArgs); }
    static void OnPreviewMouseDownThunk(Object* pSender, MouseButtonEventArgs* pArgs);
    static void OnMouseDownThunk(Object* pSender, MouseButtonEventArgs* pArgs);
    static void OnPreviewMouseUpThunk(Object* pSender, MouseButtonEventArgs* pArgs);
    static void OnMouseUpThunk(Object* pSender, MouseButtonEventArgs* pArgs);
    static void OnMouseWheelThunk(Object* pSender, MouseEventArgs* pArgs);
    static void OnMouseEnterThunk(Object* pSender, MouseEventArgs* pArgs) { ((UIElement*) pSender)->OnMouseEnter((MouseEventArgs*) pArgs); }
    static void OnMouseLeaveThunk(Object* pSender, MouseEventArgs* pArgs) { ((UIElement*) pSender)->OnMouseLeave((MouseEventArgs*) pArgs); }
    static void OnKeyDownThunk(Object* pSender, KeyEventArgs* pArgs);
    static void OnKeyUpThunk(Object* pSender, KeyEventArgs* pArgs) { ((UIElement*) pSender)->OnKeyUp((KeyEventArgs*) pArgs); }
    static void OnTextInputThunk(Object* pSender, TextCompositionEventArgs* pArgs) { ((UIElement*) pSender)->OnTextInput(pArgs); }
    static void OnQueryCursorThunk(Object* pSender, QueryCursorEventArgs* e) { ((UIElement*) pSender)->OnQueryCursor(e); }
    static void OnGotKeyboardFocusThunk(Object* pSender, KeyboardFocusChangedEventArgs* e) { ((UIElement*) pSender)->OnGotKeyboardFocus(e); }
    static void OnLostKeyboardFocusThunk(Object* pSender, KeyboardFocusChangedEventArgs* e) { ((UIElement*) pSender)->OnLostKeyboardFocus(e); }


    static void OnExecutedThunk(Object* pSender, ExecutedRoutedEventArgs* pArgs);
    static void OnPreviewExecutedThunk(Object* pSender, ExecutedRoutedEventArgs* pArgs);

    static void OnGotMouseCaptureThunk(Object* pSender, MouseEventArgs* pArgs) { ((UIElement*) pSender)->OnGotMouseCapture(pArgs); }
    static void OnLostMouseCaptureThunk(Object* pSender, MouseEventArgs* pArgs) { ((UIElement*) pSender)->OnLostMouseCapture(pArgs); }

    static void OnPreviewMouseLeftButtonDownThunk(Object* pSender, MouseButtonEventArgs* pArgs) { ((UIElement*) pSender)->OnPreviewMouseLeftButtonDown(pArgs); }
    static void OnMouseLeftButtonDownThunk(Object* pSender, MouseButtonEventArgs* pArgs) { ((UIElement*) pSender)->OnMouseLeftButtonDown(pArgs); }
    static void OnPreviewMouseLeftButtonUpThunk(Object* pSender, MouseButtonEventArgs* pArgs) { ((UIElement*) pSender)->OnPreviewMouseLeftButtonUp(pArgs); }
    static void OnMouseLeftButtonUpThunk(Object* pSender, MouseButtonEventArgs* pArgs) { ((UIElement*) pSender)->OnMouseLeftButtonUp(pArgs); }

    static void OnPreviewMouseRightButtonDownThunk(Object* pSender, MouseButtonEventArgs* pArgs) { ((UIElement*) pSender)->OnPreviewMouseRightButtonDown(pArgs); }
    static void OnMouseRightButtonDownThunk(Object* pSender, MouseButtonEventArgs* pArgs) { ((UIElement*) pSender)->OnMouseRightButtonDown(pArgs); }
    static void OnPreviewMouseRightButtonUpThunk(Object* pSender, MouseButtonEventArgs* pArgs) { ((UIElement*) pSender)->OnPreviewMouseRightButtonUp(pArgs); }
    static void OnMouseRightButtonUpThunk(Object* pSender, MouseButtonEventArgs* pArgs) { ((UIElement*) pSender)->OnMouseRightButtonUp(pArgs); }

    static void OnGotKeyboardFocus(Object* pSender, KeyboardFocusChangedEventArgs* pArgs);
    static void OnLostKeyboardFocus(Object* pSender, KeyboardFocusChangedEventArgs* pArgs);

    static void IsMouseCaptured_Changed(DependencyObject* pDO, DependencyPropertyChangedEventArgs* e);
    static void OnClipToBoundsChanged(DependencyObject* pDO, DependencyPropertyChangedEventArgs* e);
    static void OnOpacityChanged(DependencyObject* pDO, DependencyPropertyChangedEventArgs* e);
    static void OnOpacityMaskChanged(DependencyObject* pObject, DependencyPropertyChangedEventArgs* e);
    static void OnIsFocusedChanged(DependencyObject* pDO, DependencyPropertyChangedEventArgs* e);
    static void OnVisibilityChanged(DependencyObject* pObject, DependencyPropertyChangedEventArgs* e);
    static void OnIsHitTestVisibleChanged(DependencyObject* pDO, DependencyPropertyChangedEventArgs* e);
    static void OnIsEnabledChanged(DependencyObject* pDO, DependencyPropertyChangedEventArgs* e);
    static ObjectPtr CoerceIsEnabled(DependencyObject* pDO, Object* pValue);
    static void OnEffectChanged(DependencyObject* pDO, DependencyPropertyChangedEventArgs* e);

    static void OnIsVisibleChanged(DependencyObject* pDO, DependencyPropertyChangedEventArgs* e);

    static void PropagateResumeLayout(Visual* pParent, Visual* pV);
    static void PropagateSuspendLayout(Visual* pV);
    static void RenderTransform_Changed(DependencyObject* pDO, DependencyPropertyChangedEventArgs* e);
    static void RenderTransformOrigin_Changed(DependencyObject* pDO, DependencyPropertyChangedEventArgs* e);


    Visibility::Enum get_VisibilityCache() { return m_visibilityCache; }
    void set_VisibilityCache(Visibility::Enum visibility) { m_visibilityCache = visibility; UpdateIsVisibleCache(); }
    void switchVisibilityIfNeeded(Visibility::Enum visibility);
    void ensureVisible();
    void ensureInvisible(bool isCollapsed);
    void signalDesiredSizeChange();
    void pushEffect() { set_VisualEffect(get_Effect()); }
    void UpdateIsVisibleCache();

    void CrackMouseButtonEventAndReRaiseEvent(MouseButtonEventArgs* pArgs);
    void ReRaiseEventAs(RoutedEventArgs* pArgs, RoutedEvent* pNewEvent);
    static RoutedEvent* CrackMouseButtonEvent(MouseButtonEventArgs* pArgs);

    static ObjectPtr GetIsVisible(DependencyObject* pDO, BaseValueSource::Enum* pValueSource);

    static UncommonField<ItemStructList<TRefCountedPtr<CommandBinding> > > CommandBindingsField;  
    static UncommonField<SizeBox> UnclippedDesiredSizeField;  
    static UncommonField<LayoutTransformData> LayoutTransformDataField;  
    static UncommonField<EventHandler> LayoutUpdatedHandlersField;  
    static UncommonField<Object> LayoutUpdatedListItemsField;  

    ItemStructList<TRefCountedPtr<CommandBinding> >* get_CommandBindings();
    TRefCountedPtr<SizeChangedInfo> sizeChangedInfo;
    Size m_previousAvailableSize;
    Size m_desiredSize;

    Size m_renderSize;
    Rect m_finalRect;
    TRefCountedPtr<UIElement> m_pTemplateChild;

    Visibility::Enum m_visibilityCache;

    DependencyObject* m_pTemplatedParent;

    // Should this just be a bitfield struct?
    class CoreFlags
    {
    public:
        enum Enum
        {
            None =               0x0,
            NeverMeasured =      0x1,
            NeverArranged =      0x2,
            MeasureInProgress =  0x4,
            MeasureDuringArrange = 0x8,
            MeasureDirty =       0x10,

            ArrangeInProgress =  0x20,
            ArrangeDirty =       0x40,

            NeedsClipBounds =       0x80,
            InMeasureQueue  =       0x100,
            InArrangeQueue  =       0x200,
            TransformsAreClean = 0x1000,
            BypassLayoutPolicies = 0x2000,
            RenderingInvalidated = 0x4000,
            ClipToBounds = 0x8000,
            HasTemplateGeneratedSubTree = 0x10000,
            HasTemplateChildBeenOnApplied = 0x20000,
            IsRequestingExpression = 0x40000,
            IsHitTestInvisible = 0x80000,
            IsInitPending      =0x100000,
            IsInitialized      =0x200000,

            IsKeyboardFocusWithinCache =0x400000,
            IsKeyboardFocusWithinChanged =0x800000,

            IsMouseOverCache =0x1000000,
            IsMouseOverChanged =0x2000000,

            IsMouseCaptureWithinCache =0x4000000,
            IsMouseCaptureWithinChanged =0x8000000

        };
    };

    unsigned int m_templateChildIdx;
    unsigned int m_flags;
    bool m_isVisibleCache : 1;
    bool m_hasResources : 1;

};

typedef TRefCountedPtr<UIElement> UIElementPtr;
typedef TWeakPtr<UIElement> UIElementWeakPtr;


class SizeChangedInfo : public Object
{
public:
    DECLARE_ELEMENT(SizeChangedInfo, Object);

    // Methods
    SizeChangedInfo(UIElement* pElement, Size size, bool widthChanged, bool heightChanged) : m_pUIElement(pElement), m_previousSize(size), m_widthChanged(widthChanged), m_heightChanged(heightChanged) { };

    bool get_WidthChanged() { return m_widthChanged; }
    bool get_HeightChanged() { return m_heightChanged; }
    Size get_PreviousSize() { return m_previousSize; }
    Size get_NewSize() { return m_pUIElement->get_RenderSize(); }
    UIElement* get_Element() { return m_pUIElement; }

    void Update(bool widthChanged, bool heightChanged)
    {
        m_widthChanged |= widthChanged;
        m_heightChanged |= heightChanged;
    }

    TRefCountedPtr<SizeChangedInfo> Next;
private:

    TWeakPtr<UIElement> m_pUIElement;
    bool m_heightChanged;
    bool m_widthChanged;
    Size m_previousSize;

};


}; // namespace AVUI

